package com.sim.simserial.utils

import android.util.Log
import com.infinit.selfhelp.common.ext.formatWithLeadingZeros
import com.sim.serialport.library.seria.ext.amountToHex
import com.sim.serialport.library.seria.ext.asciiToHex
import com.sim.serialport.library.seria.ext.formatString
import com.sim.serialport.library.seria.ext.generate12DigitTimestamp
import com.sim.serialport.library.seria.ext.toFourLength
import com.sim.serialport.library.seria.ext.toHex
import com.sim.storage.MessageData
import com.sim.storage.RequestMessageBean
import com.sim.storage.RequestMessageHeader
import com.sim.storage.ResponseMessageBean
import com.sim.storage.ResponseMessageHeader
import com.sim.storage.enums.FieldCode
import com.sim.storage.enums.FunctionCode
import kotlin.experimental.xor

object SerialDataBuilder {
    const val STX = "02"
    const val RFU = "30"
    const val ETX = "03"
    const val SEPARATOR = "1C"
    const val VERSION_CODE = "3031"
    const val UOB = "UOB"

    /**
     * @param ecn 后台提供的，目前先使用随机值, 需要是偶數
     * */
    fun buildRequestTerminalStatus(ecn: String = asciiToHex(generate12DigitTimestamp())): String {
        val functionCode = FunctionCode.CODE_STATUS_55.code
        val versionCode = VERSION_CODE
        var messageHeaderAndData = ecn + functionCode + versionCode + RFU + SEPARATOR
        val length = (messageHeaderAndData.length / 2).formatWithLeadingZeros()
        val content = "$length$messageHeaderAndData$ETX"
        val lrc = getLrc(formatString(content))
        val commands = "$STX$content$lrc"
        Log.d("commands---", "$STX $length $ecn $functionCode $versionCode $RFU $SEPARATOR $ETX $lrc")
        return commands
    }

    fun buildLogonRequest(ecn: String = asciiToHex(generate12DigitTimestamp())): String {
        val functionCode = FunctionCode.CODE_LOGON_80.code
        val versionCode = VERSION_CODE
        var messageHeaderAndData = ecn + functionCode + versionCode + RFU + SEPARATOR
        val length = (messageHeaderAndData.length / 2).formatWithLeadingZeros()
        val content = "$length$messageHeaderAndData$ETX"
        val lrc = getLrc(formatString(content))
        val commands = "$STX$content$lrc"
        Log.d("commands---", "$STX $length $ecn $functionCode $versionCode $RFU $SEPARATOR $ETX $lrc")
        return commands
    }

    fun buildCreditCardSettlement(ecn: String = asciiToHex(generate12DigitTimestamp())): String {
        val functionCode = FunctionCode.CODE_SETTLEMENT_CREDIT_CARD_I5.code
        val versionCode = VERSION_CODE
        val messageDataS: List<MessageData> = mutableListOf<MessageData>().apply {
            add(MessageData(FieldCode.CODE_9G.code.toHex(), UOB.length.toFourLength(), UOB.toHex()))
        }

        val messageData = messageDataS.map { it.fieldCode + it.fieldLength + it.data + SEPARATOR }.joinToString("")
        var messageHeaderAndData = ecn + functionCode + versionCode + RFU + SEPARATOR + messageData
        val length = (messageHeaderAndData.length / 2).formatWithLeadingZeros()

        val content = "$length$messageHeaderAndData$ETX"
        val lrc = getLrc(formatString(content))
        val commands = "$STX$content$lrc"

        Log.d("commands---", "$STX $length $ecn $functionCode $versionCode $RFU $SEPARATOR $messageData $ETX $lrc")
        return commands
    }

    fun buildNETSSettlement(ecn: String = asciiToHex(generate12DigitTimestamp())): String {
        val functionCode = FunctionCode.CODE_SETTLEMENT_NETS_81.code
        val versionCode = VERSION_CODE
        var messageHeaderAndData = ecn + functionCode + versionCode + RFU + SEPARATOR
        val length = (messageHeaderAndData.length / 2).formatWithLeadingZeros()
        val content = "$length$messageHeaderAndData$ETX"
        val lrc = getLrc(formatString(content))
        val commands = "$STX$content$lrc"
        Log.d("commands---", "$STX $length $ecn $functionCode $versionCode $RFU $SEPARATOR $ETX $lrc")
        return commands

    }

    fun buildTMSRequest(ecn: String = asciiToHex(generate12DigitTimestamp())): String {
        val functionCode = FunctionCode.CODE_TMS_84.code // 84
        val versionCode = VERSION_CODE
        var messageHeaderAndData = ecn + functionCode + versionCode + RFU + SEPARATOR
        val length = (messageHeaderAndData.length / 2).formatWithLeadingZeros()
        val content = "$length$messageHeaderAndData$ETX"
        val lrc = getLrc(formatString(content))
        val commands = "$STX$content$lrc"
        Log.d("commands---", "$STX $length $ecn $functionCode $versionCode $RFU $SEPARATOR $ETX $lrc")
        return commands
    }

    fun buildLastApprovedRequest(ecn: String = asciiToHex(generate12DigitTimestamp())): String {
        val functionCode = FunctionCode.CODE_GET_LAST_APPROVED_TRANSACTION_56.code
        val versionCode = VERSION_CODE
        var messageHeaderAndData = ecn + functionCode + versionCode + RFU + SEPARATOR
        val length = (messageHeaderAndData.length / 2).formatWithLeadingZeros()
        val content = "$length$messageHeaderAndData$ETX"
        val lrc = getLrc(formatString(content))
        val commands = "$STX$content$lrc"
        Log.d("commands---", "$STX $length $ecn $functionCode $versionCode $RFU $SEPARATOR $ETX $lrc")
        return commands
    }

    fun buildSingleEntryPurchase(ecn: String = asciiToHex(generate12DigitTimestamp()), amount: Float): String {
        val functionCode = "3030"
        val versionCode = VERSION_CODE
        val messageDataS: List<MessageData> = mutableListOf<MessageData>().apply {
            add(MessageData("3430", "0012", asciiToHex(amount.toString(), 12)))
        }

        val messageData = messageDataS.map { it.fieldCode + it.fieldLength + it.data + SEPARATOR }.joinToString("")
        var messageHeaderAndData = ecn + functionCode + versionCode + RFU + SEPARATOR + messageData
        val length = (messageHeaderAndData.length / 2).formatWithLeadingZeros()

        val content = "$length$messageHeaderAndData$ETX"
        val lrc = getLrc(formatString(content))
        val commands = "$STX$content$lrc"

        Log.d("commands---", "$STX $length $ecn $functionCode $versionCode $RFU $SEPARATOR $messageData $ETX $lrc")
        return commands
    }

    fun buildAllNetsPurchaseRequest(ecn: String = asciiToHex(generate12DigitTimestamp()), amount: Float, transactionType: String): String {
        val functionCode = "3330"
        val versionCode = VERSION_CODE

        val messageDataS: List<MessageData> = mutableListOf<MessageData>().apply {
            add(MessageData(FieldCode.CODE_T2.code.toHex(), "0002", transactionType.toHex()))
            add(MessageData(FieldCode.CODE_40.code.toHex(), "0012", amount.toString().amountToHex()))
        }

        val messageData = messageDataS.map { it.fieldCode + it.fieldLength + it.data + SEPARATOR }.joinToString("")
        var messageHeaderAndData = ecn + functionCode + versionCode + RFU + SEPARATOR + messageData
        val length = (messageHeaderAndData.length / 2).formatWithLeadingZeros()

        val content = "$length$messageHeaderAndData$ETX"
        val lrc = getLrc(formatString(content))
        val commands = "$STX$content$lrc"

        Log.d("commands---", "$STX $length $ecn $functionCode $versionCode $RFU $SEPARATOR $messageData $ETX $lrc")
        return commands
    }

    fun buildCreditCardSaleRequest(ecn: String = asciiToHex(generate12DigitTimestamp()), amount: Float): String {
        val functionCode = FunctionCode.CODE_I0.code
        val versionCode = VERSION_CODE

        val messageDataS: List<MessageData> = mutableListOf<MessageData>().apply {
            add(MessageData(FieldCode.CODE_40.code.toHex(), "0012", amount.toString().amountToHex()))
        }

        val messageData = messageDataS.map { it.fieldCode + it.fieldLength + it.data + SEPARATOR }.joinToString("")
        var messageHeaderAndData = ecn + functionCode + versionCode + RFU + SEPARATOR + messageData
        val length = (messageHeaderAndData.length / 2).formatWithLeadingZeros()

        val content = "$length$messageHeaderAndData$ETX"
        val lrc = getLrc(formatString(content))
        val commands = "$STX$content$lrc"

        Log.d("commands---", "$STX $length $ecn $functionCode $versionCode $RFU $SEPARATOR $messageData $ETX $lrc")
        return commands
    }

    fun buildCreditCardSaleVoid(ecn: String = asciiToHex(generate12DigitTimestamp()), transactionId:String, processingGateway:String): String {
        val functionCode = FunctionCode.CODE_CREDIT_CARD_VOID_I1.code
        val versionCode = VERSION_CODE

        val messageDataS: List<MessageData> = mutableListOf<MessageData>().apply {
            add(MessageData(FieldCode.CODE_A1.code.toHex(), transactionId.length.toFourLength(), transactionId.toHex()))
            add(MessageData(FieldCode.CODE_D7.code.toHex(), processingGateway.length.toFourLength(), processingGateway.toHex()))

        }

        val messageData = messageDataS.map { it.fieldCode + it.fieldLength + it.data + SEPARATOR }.joinToString("")
        var messageHeaderAndData = ecn + functionCode + versionCode + RFU + SEPARATOR + messageData
        val length = (messageHeaderAndData.length / 2).formatWithLeadingZeros()

        val content = "$length$messageHeaderAndData$ETX"
        val lrc = getLrc(formatString(content))
        val commands = "$STX$content$lrc"

        Log.d("commands---", "$STX $length $ecn $functionCode $versionCode $RFU $SEPARATOR $messageData $ETX $lrc")
        return commands
    }


    /**
     * @param requestData format is ** ** ** ** **
     * */
    fun analysisRequestData(requestData: String): RequestMessageBean? {
        val stringList = requestData.split(" ")
        if (stringList.size < 10) {
            return null
        }
        val stx = stringList[0]
        var messageHeaderLength = (stringList[1] + stringList[2])
        var length = messageHeaderLength.toInt()

        var messageHeaderAndData = stringList.subList(3, length + 3) // messageHeader+ messageData

        val indexFirst1C = messageHeaderAndData.indexOfFirst { it == SEPARATOR }
        var messageHeader = messageHeaderAndData.subList(0, indexFirst1C + 1)

        var messageDataBean = mutableListOf<MessageData>()
        if (messageHeaderAndData.size > indexFirst1C + 2) {
            // 存在 message Data
            var messageData = listOf<String>()
            messageData = messageHeaderAndData.subList(indexFirst1C + 1, messageHeaderAndData.size)

            val split = messageData.joinToString("").split(SEPARATOR)
            split.forEach {
                if (it.isNotEmpty()) {
                    val messageData1 = MessageData(it.substring(0, 4), it.substring(4, 8), it.substring(8))
                    messageDataBean.add(messageData1)
                }
            }
        }

        val ecn: String = messageHeader.subList(0, messageHeader.size - 6).joinToString("")
        val functionCode: String =
            messageHeader.subList(messageHeader.size - 6, messageHeader.size - 4).joinToString("")
        val responseCode: String =
            messageHeader.subList(messageHeader.size - 4, messageHeader.size - 2).joinToString("")
        val rfu = messageHeader[messageHeader.size - 2]

        val etx = stringList[stringList.size - 2]
        val lcr = stringList.last().toString()

        val messageBean = RequestMessageBean(stx, "$length", RequestMessageHeader(ecn, functionCode, responseCode, rfu), messageDataBean, etx, lcr)
        return messageBean
    }


    fun analysisReceiveData(receiveData: String): ResponseMessageBean? {
        // 02 00 18 30 30 30 30 30 30 30 30 35 37 36 34 35 35 30 30 30 1C 03 37
        val stringList = receiveData.split(" ")

        if (stringList.size < 10) {
            return null
        }

        val stx = stringList[0]
        var messageHeaderLength = (stringList[1] + stringList[2])
        var length = messageHeaderLength.toInt()

        var messageHeaderAndData = stringList.subList(3, length + 3) // messageHeader+ messageData

        val indexFirst1C = messageHeaderAndData.indexOfFirst { it == SEPARATOR }
        var messageHeader = messageHeaderAndData.subList(0, indexFirst1C + 1)

        var messageDataBean = mutableListOf<MessageData>()
        if (messageHeaderAndData.size > indexFirst1C + 2) {
            // 存在 message Data
            var messageData = listOf<String>()
            messageData = messageHeaderAndData.subList(indexFirst1C + 1, messageHeaderAndData.size)

            val split = messageData.joinToString("").split(SEPARATOR)
            split.forEach {
                if (it.isNotEmpty()) {
                    val messageData1 = MessageData(it.substring(0, 4), it.substring(4, 8), it.substring(8))
                    messageDataBean.add(messageData1)
                }
            }
        }

        val ecn: String = messageHeader.subList(0, messageHeader.size - 6).joinToString("")
        val functionCode: String =
            messageHeader.subList(messageHeader.size - 6, messageHeader.size - 4).joinToString("")
        val responseCode: String =
            messageHeader.subList(messageHeader.size - 4, messageHeader.size - 2).joinToString("")
        val rfu = messageHeader[messageHeader.size - 2]

        val etx = stringList[stringList.size - 2]
        val lcr = stringList.last().toString()

        val messageBean = ResponseMessageBean(stx, "$length", ResponseMessageHeader(ecn, functionCode, responseCode, rfu), messageDataBean, etx, lcr)
        return messageBean
    }

    fun checkLRC(byteArray: ByteArray): Byte {
        var lrc: Byte = 0x00
        for (byte in byteArray) {
            lrc = lrc xor byte
        }
        return lrc
    }

    /**
     * @param hexString : without STX(02) and LRC
     * */
    fun getLrc(hexString: String): String {
        var stringList = hexString.split(" ").toMutableList()
        val data = stringList.map { it.toInt(16).toByte() }
        val relevantData = data.toByteArray()
        var lrc: Byte = 0x00
        for (byte in relevantData) {
            lrc = lrc xor byte
        }
        return String.format("%02X", lrc)
    }

    /**
     * @param hexString intact commands, contain STX(02) and LRC. format is ** ** ** ** ** ** **
     * */
    fun checkLRC(hexString: String): Boolean {
        var stringList = hexString.split(" ").toMutableList()
        if (stringList.size < 3) {
            return false
        }
        stringList.removeFirst()
        val currentLrc = stringList.removeLast()

        val data = stringList.map { it.toInt(16).toByte() }

        // Remove the first (STX) and last (LRC) bytes
        val relevantData = data.toByteArray()

        var lrc: Byte = 0x00

        for (byte in relevantData) {
            lrc = lrc xor byte
        }
        val format = String.format("%02X", lrc)


        return currentLrc == format
    }


}
